package org.jeecg.modules.wechat.utils; /***
 *
 *包都在这
 *
 *
 *
 */

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jeecg.common.base.BaseMap;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WXPaySignatureCertificateUtil {


    private static final ConcurrentHashMap<String, Verifier> verifierMaps = new ConcurrentHashMap<>();


    /**
     * 证书验证
     * 自动更新的签名验证器
     */
    public static CloseableHttpClient checkSign(String mchSerialNo, String Mch_ID, String apiV3Key) throws IOException {
        //验签
        CloseableHttpClient httpClient = null;
        PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey(Mch_ID);

        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(Mch_ID, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifiers(mchSerialNo, Mch_ID, apiV3Key)))
                .build();

        return httpClient;
    }


    /**
     * 功能描述:获取平台证书，自动更新
     * 注意：这个方法内置了平台证书的获取和返回值解密
     */
    public static Verifier getVerifiers(String mchSerialNo, String Mch_ID, String apiV3Key) {
        Verifier verifier = null;
        if (verifierMaps.isEmpty() || !verifierMaps.containsKey(mchSerialNo)) {
            verifierMaps.clear();
            try {
                PrivateKey privateKey = getPrivateKey(Mch_ID);
                //刷新
                PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
                WechatPay2Credentials credentials = new WechatPay2Credentials(Mch_ID, signer);
                verifier = new AutoUpdateCertificatesVerifier(credentials
                        , apiV3Key.getBytes("utf-8"));
                verifierMaps.put(verifier.getValidCertificate().getSerialNumber() + "", verifier);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            verifier = verifierMaps.get(mchSerialNo);
        }
        return verifier;
    }


    /**
     * app生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String appPaySign(String APP_ID, String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey("");
        String signatureStr = Stream.of(APP_ID, timestamp, nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 小程序及其它支付生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String jsApiPaySign(String APP_ID, String timestamp, String nonceStr, String prepayId,String mchId )throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey(mchId);
        String signatureStr = Stream.of(APP_ID, timestamp, nonceStr, "prepay_id=" + prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * 获取私钥。
     * 证书路径 本地使用如： D:\\微信平台证书工具\\7.9\\apiclient_key.pem
     * 证书路径 线上使用如： /usr/apiclient_key.pem
     * String filename 私钥文件路径  (required)
     *
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String Mch_ID) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get("C:\\web_server\\apiclient_key.pem")), "utf-8");
        if (Mch_ID!=null && "1526566731".equals(Mch_ID)) { //安好商户号
            content = new String(Files.readAllBytes(Paths.get("C:\\web_serverah\\apiclient_key.pem")), "utf-8");
        }
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }


    public static BaseMap jsApiPay(BaseMap map, String appid) throws Exception {

        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = RandomStringUtils.randomAlphanumeric(32);
        String paySign = jsApiPaySign(appid, timestamp, nonceStr, map.get("prepayid").toString(),map.get("mchId"));

        map.put("prepayid", map.get("prepayid").toString());
        map.put("package", "prepay_id=" + map.get("prepayid").toString());
        map.put("noncestr", nonceStr);
        map.put("timestamp", timestamp);
        map.put("sign", paySign);
        map.put("code", 200);

        return map;
    }

}